package com.zh.note.leetcode.stackqueue;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

/**
 * 239. 滑动窗口最大值
 * 给你一个整数数组 nums，有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
 * 返回 滑动窗口中的最大值 。
 * 输入：nums = [1,3,-1,-3,5,3,6,7], k = 3
 * 输出：[3,3,5,5,6,7]
 * 解释：
 * 滑动窗口的位置                最大值
 * ---------------               -----
 * [1  3  -1] -3  5  3  6  7       3
 * 1 [3  -1  -3] 5  3  6  7       3
 * 1  3 [-1  -3  5] 3  6  7       5
 * 1  3  -1 [-3  5  3] 6  7       5
 * 1  3  -1  -3 [5  3  6] 7       6
 * 1  3  -1  -3  5 [3  6  7]      7
 */
public class LC_239_maxSlidingWindow {
    /**
     * 自定义单调队列
     */
    class Myqueue {
        private Deque<Integer> deque = new LinkedList<>();

        //出队
        public void poll(int val) {
            //弹出元素时，比较当前要弹出的数值是否等于队列出口的数值，如果相等则弹出
            //同时判断队列当前是否为空
            if (!deque.isEmpty() && deque.peekFirst() == val) {
                // 移除队首元素
                deque.pollFirst();
            }
        }

        // 入队
        public void push(int val) {
            //添加元素时，如果要添加的元素大于入口处的元素，就将入口元素弹出
            //保证队列元素单调递减
            //比如此时队列元素3,1，2将要入队，比1大，所以1弹出，此时队列：3,2
            while (!deque.isEmpty() && val > deque.peekLast()) {
                deque.removeLast();
            }
            deque.addLast(val);
        }

        // 获取队最大元素，也就是队列头部的元素
        public int getMaxValue() {
            return deque.peek();
        }
    }

    public int[] maxSlidingWindow(int[] nums, int k) {
        Myqueue myqueue = new Myqueue();
        List<Integer> result = new ArrayList<>();
        // 先将前k的元素放进队列
        for (int i = 0; i < k; i++) {
            myqueue.push(nums[i]);
        }
        // result 记录前k的元素的最大值
        result.add(myqueue.getMaxValue());
        for (int i = k; i < nums.length; i++) {
            //滑动窗口移除最前面的元素，移除是判断该元素是否放入队列
            myqueue.poll(nums[i - k]);
            //滑动窗口加入最后面的元素
            myqueue.push(nums[i]);
            result.add(myqueue.getMaxValue());
        }
        return result.stream().mapToInt(v -> v).toArray();
    }
}
